package com.dog.framework.security.aspect;

import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import com.dog.framework.core.constant.CacheConstants;
import com.dog.framework.core.exception.ServiceException;
import com.dog.framework.core.utils.DecodeUtils;
import com.dog.framework.security.annotation.RepeatSubmit;
import com.dog.framework.security.config.HttpRequestWrapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 公众号：码猿技术专栏
 * @url: www.java-family.cn
 * @description 防重复点击注解切面
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class RepeatSubmitAspect implements Ordered {


	private final StringRedisTemplate stringRedisTemplate;

	@SneakyThrows
	@Around("@within(repeatSubmit) || @annotation(repeatSubmit)")
	public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) {
		HttpServletRequest request = ((ServletRequestAttributes) Objects
				.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

		//请求的URL
		String requestURI = URLUtil.getPath(request.getRequestURI());
		//请求的IP地址
		String clientIP = ServletUtil.getClientIP(request);
		//请求的参数
		String params = getParams(request);

		//获取参数
		String key= MessageFormat.format(CacheConstants.REPEAT_SUBMIT_KEY,requestURI,Math.abs(clientIP.hashCode()),
				Math.abs(DecodeUtils.md5(params).hashCode()));
		//存在即返回false，不存在即返回true
		Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, "", repeatSubmit.seconds(), TimeUnit.SECONDS);
		if(!ifAbsent){
			throw new ServiceException("请勿重复提交！！！");
		}
		return joinPoint.proceed();
	}


	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE + 3;
	}

	private String getParams(HttpServletRequest request) throws Exception {
		String params;
		if (request instanceof HttpRequestWrapper){
			params = request.getReader().readLine();
		}else {
			params= HttpUtil.toParams(request.getParameterMap());
		}
		return params;
	}
}
